using UnityEngine;
using System.Collections;
using ProtoBuf;
using System.Reflection;

/*
SerializeUtility.cs
Jhoemar Pagao
jhoemar.pagao@gmail.com
All rights Reserved

Class the facilitates serialization and deserialization.

 */
public class SerializeUtility {

	static ProtoBuf.Meta.TypeModel mModel;
	static bool mIsInit=false;
	
	public static byte[] Serialize( object pObject)
	{
		if( !mIsInit )
			InitModel();

		byte[] results = null;
		System.IO.MemoryStream stream = new System.IO.MemoryStream();
		mModel.Serialize(stream, pObject);
		results = stream.GetBuffer();
		stream.Close();
		return results;
	}

	public static object Deserialize( System.Type pType, byte[] pBytes)
	{
		InitModel();

		System.IO.MemoryStream stream = new System.IO.MemoryStream(pBytes);
		object obj = mModel.Deserialize( stream, null, pType);
		stream.Close();

		return obj;
	}

	//use to save the bytes to specified location
	public static void SaveBytes ( string pLocation, byte[] pBytes)
	{
		InitModel();

		System.IO.FileStream fStream = new System.IO.FileStream( pLocation ,System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write);
		fStream.Write( pBytes, 0, pBytes.Length);
		fStream.Close();
	}

	//use to load object from file
	public static object LoadObject (string pLocation, System.Type pType)
	{
		if( !mIsInit )
			InitModel();

		System.IO.FileStream fS = new System.IO.FileStream( pLocation, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Read);
		if( fS.Length > 0 )
		{
			byte[] bytes = new byte[(int)fS.Length];
			fS.Read( bytes, 0, (int)fS.Length);
			fS.Close();

			return Deserialize( pType, bytes);
		}else
		{
			fS.Close();
			return null;
		}
	}

	//use to save the object
	//@param1: location which the object will be saved
	//@param2: the object to be save
	public static void SaveObject (string pLocation, object pData)
	{
		SaveBytes( pLocation, Serialize( pData));
	}

	//use to check whether the file exist
	//@return: true if the location is exist false if fnot
	public static bool IsFileExist( string pLocation )
	{
		return System.IO.File.Exists( pLocation);
	}
	
	public static string BytesToString( byte[] pBytes)
	{
		return System.Convert.ToBase64String( pBytes);
	}
	
	public static byte[] StringToBytes( string pString)
	{
		return System.Convert.FromBase64String( pString);
	}

#if UNITY_EDITOR
	//directories
	public static string mSerializerDIR = System.IO.Directory.GetCurrentDirectory()+"/Assets/CustomSerializer1.4";
	public static string mPROTOBUFF_FULL = mSerializerDIR + "/Editor/protobufFULL.dll";
	public static string mDestinationBuild = mSerializerDIR + "/Plugins/";
#endif

	//use to initialize serialization
	//@param: the types to be included on the serialization. should have follow the protocontract protocol
	public static void Init( params System.Type[] pTypes)
	{
#if (!UNITY_IPHONE && !UNITY_ANDROID) || UNITY_EDITOR

		//delete old dll
		if( System.IO.File.Exists(mSerializerDIR + "/Plugins/MySerializer.dll") )
			System.IO.File.Delete(mSerializerDIR + "/Plugins/MySerializer.dll");

		string pLibraryName = "MySerializer";

		Assembly assembly;
		assembly = Assembly.LoadFile( mPROTOBUFF_FULL);

		pLibraryName += "." + "dll";

		System.Reflection.MethodInfo k = assembly.GetType("ProtoBuf.Meta.TypeModel").GetMethod("Create",BindingFlags.Public|BindingFlags.Static);
		System.Type t = assembly.GetType("ProtoBuf.Meta.TypeModel");
		object model = t.GetMethod( "Create", BindingFlags.Public|BindingFlags.Static).Invoke( null, null);

		System.Reflection.MethodInfo methodAdd = model.GetType().GetMethod( "Add", new System.Type[]{ typeof(System.Type), typeof(bool)});
		for( int i=0; i<pTypes.Length; i++)
			methodAdd.Invoke( model, new object[]{pTypes[i], true});
		
		if( System.IO.File.Exists( mDestinationBuild + pLibraryName))
		{
			Debug.Log( "Updated! " + pLibraryName);
			UnityEditor.FileUtil.DeleteFileOrDirectory( mDestinationBuild + pLibraryName);
		}else
			Debug.Log("Compiling @output: " + mDestinationBuild);
		
		model.GetType().GetMethod("Compile", new System.Type[]{typeof(string), typeof(string)}).Invoke( model, new string[]{ "MySerializer", pLibraryName});
		System.IO.Directory.Move( System.IO.Directory.GetCurrentDirectory() + "/" + pLibraryName, mDestinationBuild + pLibraryName);
		
		assembly = null;
		UnityEditor.AssetDatabase.Refresh();
#endif
		InitModel();
	}

	static void InitModel()
	{
		if( mIsInit )
			return;

		mIsInit = true;
		//initialize the model from the generated type
		mModel = (ProtoBuf.Meta.TypeModel)System.Activator.CreateInstance( System.Type.GetType("MySerializer, MySerializer, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"));
	}
}